/*
媒体播放器
*/

class MediaPlayer {
	public:
		int readPackets();		//读取数据包
		int prepareDecoder(int streamIndex);	//解码器初始化
		int openAudioDevice(int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate);		//打开音频设备
}

int MediaPlayer::prepareDecoder(int streamIndex) {
	AVCodecContext *avctx;
	AVCodec *codec;
	AVDictionary *opts = NULL;
	AVDictionaryEntry *t = NULL;
	int ret = 0;
	const char* forcedCodecName = NULL;

	if(streamIndex < 0 || streamIndex >= pFormatCtx->nb_streams) {
		return -1;
	}
	avctx = avcodec_alloc_context3(NULL);
	if(!avctx){
		return AVERROR(ENOMEM);
	}

	do {
		//复制解码上下文
		ret = avcodec_parameters_to_context(avctx, pFormatCtx->streams[streamIndex]->codecpar);
		if(ret < 0){
			break;
		}
		// 设置时钟基准
		av_codec_set_pkt_timebase(avctx, pFormatCtx->streams[streamIndex]->time_base);
		// 查找解码器
		codec = avcodec_find_decoder(avctx->codec_id);
		//指定解码器
		switch(avctx->codec_type){
			case AVMEDIA_TYPE_AUDIO: 
				forcedCodecName = playerState->audioCodecName;
				break;

			case AVMEDIA_TYPE_VIDEO:
				forcedCodecName = playerState->videoCodecName;
				break;
		}
		//为什么要强制指定解码器呢？？？？ 不知道
		if(forcedCodecName){
			codec = avcodec_find_decoder_by_name(forcedCodecName);
		}
		// 判断是否成功得到解码器
		if (!codec) {
		    av_log(NULL, AV_LOG_WARNING,
			   "No codec could be found with id %d\n", avctx->codec_id);
		    ret = AVERROR(EINVAL);
		    break;
		}
		avctx->codec_id = codec->id;
		//设置播放参数
		int stream_lowres = playerState->lowres;
		if(stream_lowres > av_codec_get_max_lowres(codec)){
			av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
                   av_codec_get_max_lowres(codec));
		        stream_lowres = av_codec_get_max_lowres(codec);
		}
		av_codec_set_lowres(avctx, stream_lowres);
#if FF_API_EMU_EDGE
		if(stream_lowres) {
			avctx->flags |= CODEC_FLAG_EMU_EDGE;
		}
#endif
		if(playerState->fast){
			avctx->flags2 != AV_CODEC_FLAG2_FAST;
		}
#if FF_API_EMU_EDGE
		if(codec->capabilities & AV_CODEC_CAP_DR1) {
			avctx->flags |= CODEC_FLAG_EMU_EDGE;
		}
#endif
		opts = filterCodecOptions(playerState->codec_opts, avctx->codec_id, pFormatCtx, pFormatCtx->streams[streamIndex], codec);
		if (!av_dict_get(opts, "threads", NULL, 0)) {
		    av_dict_set(&opts, "threads", "auto", 0);
		}
		if (stream_lowres) {
		    av_dict_set_int(&opts, "lowres", stream_lowres, 0);
		}
		if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
		    av_dict_set(&opts, "refcounted_frames", "1", 0);
		}
		// 打开解码器
		if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {
		    break;
		}
		if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
		    av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
		    ret =  AVERROR_OPTION_NOT_FOUND;
		    break;
		}
		// 根据解码器类型创建解码器
		pFormatCtx->streams[streamIndex]->discard = AVDISCARD_DEFAULT;
		switch (avctx->codec_type) {
		    case AVMEDIA_TYPE_AUDIO: {
			audioDecoder = new AudioDecoder(avctx, pFormatCtx->streams[streamIndex],
							streamIndex, playerState);
			break;
		    }

		    case AVMEDIA_TYPE_VIDEO: {
			videoDecoder = new VideoDecoder(pFormatCtx, avctx, pFormatCtx->streams[streamIndex],
							streamIndex, playerState);
			attachmentRequest = 1;
			break;
		    }

		    default:{
			break;
		    }
		}
	}while(false);
	// 准备失败，则需要释放创建的解码上下文
	    if (ret < 0) {
		if (playerCallback != NULL) {
		    playerCallback->onError(0x01, "failed to open stream!");
		}
		avcodec_free_context(&avctx);
	    }

	    // 释放参数
	    av_dict_free(&opts);

	    return ret;
}	


int MdiaPlayer::readPackets() {
	int ret = 0;
	AVDictionaryEntry *t;
	AVDictionary **opts;

	//准备解码器
	mMutex.lock();
	do {
		pFormatCtx = avformat_alloc_context();
		if(!pFormatCtx) {
			av_log(NULL, AV_LOG_FATAL, "could not alloc av format context");
			//空间不足错误变量
			ret = AVERROR(ENOMEM);
			break;
		}

		//设置解码复用中段回调 IO层中断回调，callback是一个int (*callback)(void*);类型的函数指针,必须在打开输入流之前设置，触发是读取输入数据时触发
		pFormatCtx->interrupt_callback.callback = avformat_interrupt_cb;
		pFormatCtx->interrupt_callback.opaque = playerState;  //不知道
		//format_opt是两个数据结构嵌套组合，
		/**
		typedef struct AVDictionaryEntry {
		    char *key;
		    char *value;
		} AVDictionaryEntry;

		struct AVDictionary {
		    int count;
		    //可以看成一个动态数组
		    AVDictionaryEntry *elems;
		};
		av_dic_get(dictory, key, AVDictionaryEntry, flag)  从dictory中找到key的AVDictionaryEntry， 因为AVDictionaryEntry是一个数组，所以第三个参数表示从这个数组的index下开始查找，
		第4个参数表示匹配key时大小写敏感、只匹配前缀等，如果没找到就返回NULL
		av_dic_set(dictory, key, vlue, flag) 从dictory添加key,value，没有就会创建 注意set的第一个参数是二级指针
		flag标志位很重要，如下面的是不允许复写，在来一个不会复写
		*/
		if(!av_dic_get(playerState->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
			av_dic_set(&playerState->format_opts, "scan_all_pmts", 1, AV_DICT_DONT_OVERWRITE);
			scan_all_pmts_set = 1;
		}

		//设置rtmp/rtsp的超时值
		/**
		av_stristart(p1, p2, p3) 字符串比较判断，p2等于p1的前缀，大小写不敏感，匹配成功返回非0， p3指针指向匹配结果后的字符串
		*/
		if(av_stristart(url, "rtmp", NULL) || av_stristart(url, "rtsp", NULL)){
			av_log(NULL, AV_LOG_WARNING, "remove 'timeout' option for rtmp.\n");
		        av_dict_set(&playerState->format_opts, "timeout", NULL, 0);
		}

		//打开流媒体
		ret = avformat_open_input(&pFormatCtx, url, NULL, &playerState->format_opts);
		if(ret < 0){
			av_log(NULL, AV_LOG_WARNING, "open input url error\n");
			printError(ret);
			ret = -1;
			break;
		}
		if(scan_all_pmts_set) {
			av_dic_set(&playerState->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);
		}

		if ((t = av_dict_get(playerState->format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {
		    av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);
		    ret = AVERROR_OPTION_NOT_FOUND;
		}
		if(playerState->genpts) {
			pFormatCtx->flags |= AVFMT_FLAG_GENPTS;
		}
		//该行数作用将AVFormateContext下所有的AVStream内部的边界数据置为1，但是有什么用呢  不知道
		av_format_inject_global_side_data(pFormatCtx);
		
		opts = setupStreamInfoOptions(pFormatCtx, playerState->codec_opts);  //源码中没找到这个方法
		//查找媒体流信息
		ret = avformat_find_stream_info(pFormatCtx, opts);
		if(opts != NULL){
			for (int i = 0; i < pFormat->nb_streams, i++){
				if(opts[i] != NULL)
					av_dict_free(&opts[i]);
			}
			av_dict_free(&opts);
		}
		if(ret < 0) {
		   av_log(NULL, AV_LOG_WARNING, "%s: could not find codec parameters\n", url);
		    ret = -1;
		    break;
		}

		// 文件时长(秒)
		if (pFormatCtx->duration != AV_NOPTS_VALUE) {
		    mDuration = (int)(pFormatCtx->duration / AV_TIME_BASE);
		}
		//pb类型为AVIOContext,IO读取
		if(pFormatCtx->pb){
			pFormatCtx->pb->eof_reached = 0;  //设置读取为0
		}
		//iformat就是一个AVInputFormat 不知道这么比较为什么可以确定是否字节定位
		playerState->seekByBytes = !!(pFormat->iformat->flags & AVFMT_TS_DISCONT) &&  strcmp("ogg", pFormatCtx->iformat->name);

		//设置最大帧间隔
		mediaSync->setMaxDuration((pFormatCtx->iformat->flags * AVFMT_TS_DISCONT) ? 10.0 : 3600.0);
		//如果不是从头播放就需要跳转帧
		if(palyerState->startTime != AV_NOPTS_VALUE){
			int64_t timestamp = payerState->startTime;
			if(pFormatCtx->startTime != AV_NOPTS_VALUE) {
				timestamp += pFormatCtx->startTime;
			}
			//定位帧
			/*
			 avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
			 stream_index 那个流定位 ，-1表示对所有流
			 ts定位位置
			 flags是以字节或者frame帧进行定位
			*/
			ret = avformat_seek_file(pFormatCtx, -1, INT64_MIN, timestamp, INT64_MAX, 0);
			if (ret < 0) {
				av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",url, (double)timestamp / AV_TIME_BASE);
			}
		}
		// 判断是否实时流，判断是否需要设置无限缓冲区
		playerState->realTime = isRealTime(pFormatCtx);
		if(playerState->realTime && playerState->infiniteBuffer < 0) {
			playerState->infiniteBuffer = 1;
		}
		//查找媒体流信息
		int audioIndex = -1;
		int videoIndex = -1;
		for(int i = 0 ; i < pFormatCtx->nb_streams; i++) {
			//音频 AVCodecParameters *codecpar; AVMediaType codec_type
			/**
			enum AVMediaType {
			    AVMEDIA_TYPE_UNKNOWN = -1,  ///< Usually treated as AVMEDIA_TYPE_DATA
			    AVMEDIA_TYPE_VIDEO,
			    AVMEDIA_TYPE_AUDIO,
			    AVMEDIA_TYPE_DATA,          ///< Opaque data information usually continuous
			    AVMEDIA_TYPE_SUBTITLE,
			    AVMEDIA_TYPE_ATTACHMENT,    ///< Opaque data information usually sparse
			    AVMEDIA_TYPE_NB
			};
			*/
			if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
				if(audioIndex == -1){
					audioIndex = i;
				}
			}else if(pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
				if(videoIndex == -1){
					videoIndex = i;
				}
			}
		}
		
		if(!playerState->videoDisable){
			//查找最优质的视频流
			videoIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1 , -1, NULL, 0);
		}else{
			videoIndex = -1;
		}

		if(!playerState->audioDisable){
			//查找最优质的视频流
			audioIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1 , -1, NULL, 0);
		}else{
			audioIndex = -1;
		}
		if(audioIndex == -1 && videoIndex == -1){
			av_log(NULL, AV_LOG_WARNING,
                   "%s: could not find audio and video stream\n", url);
			 ret = -1;
			 break;
		}

		if(videoIndex >= 0) {
			prepareDecoder(videoIndex);
		}
		if(audioIndex > = 0){
			prepareDecoder(audioIndex);
		}

		if (!audioDecoder && !videoDecoder) {
		    av_log(NULL, AV_LOG_WARNING,
			   "failed to create audio and video decoder\n");
		    ret = -1;
		    break;
		}
		ret = 0;
	}while(false);
	mMutex.unlock();

	if(ret < 0){
		if(playerCallback) {
			playerCallback.onError(0x01, "prepare decoder failed!");
		}
		return -1;
	}

	//准备成功回调
	if(playerCallback){
		playerCallback-onPrepared();
	}

	if(videoDecoder != NULL){
		videoDecoder->start();
	}else{
		if(playerState->SyncType == AV_SYNC_VIDEO) {
			playerState->syncType = AV_SYNC_AUDIO;
		}
	}
	if(audioDecoder != NULL) {
		audioDecoder->start();
	}else{
		if(playerState->SyncType == AV_SYNC_AUDIO) {
			playerState->syncType = AV_SYNC_EXTERNAL;
		}
	}

	//打开音视频输出设备
	if(audioDecoder != NULL){
		AVCodecContext *avctx = audioDecoder->getCodecContext();
		ret = openAudioDevice(avctx->channel_layout, avctx->channels, avctx->sample_rate);
		if(ret < 0){
			av_log(NULL, AV_LOG_WARNING, "could not open audio device\n");
			// 如果音频设备打开失败，则调整时钟的同步类型
			if (playerState->syncType == AV_SYNC_AUDIO) {
			if (videoDecoder != NULL) {
			    playerState->syncType = AV_SYNC_VIDEO;
			} else {
			    playerState->syncType = AV_SYNC_EXTERNAL;
			}
		    }
		}else{
			//打开
			audioDevice->start();
		}
	}

	if(videoDeoder != NULL){
		if (playerState->syncType == AV_SYNC_AUDIO) {
		    videoDecoder->setMasterClock(mediaSync->getAudioClock());
		} else if (playerState->syncType == AV_SYNC_VIDEO) {
		    videoDecoder->setMasterClock(mediaSync->getVideoClock());
		} else {
		    videoDecoder->setMasterClock(mediaSync->getExternalClock());
		}
	}
	//开始同步
	mediaSync->start(videoDecoder, audioDecoder);

	//读取数据包
	ret = 0;
	eof = 0;
	AVPacket pkt1, *pkt = &pkt1;
	int64_t stream_start_time;
	int playInRange = 0;
	int64_t pkt_ts;
	for(;;){
		//退出播放器
		if(playerState->abortRequest){
			break;
		}

		if(playerState->pauseRequest != lastPaused){
			lastRaused = playerState->pauseRequest;
			if(playerState->pauseRequest){
				av_read_pause(pFormatCtx);
			}else{
				av_read_play(pFormatCtx);
			}
		}
#if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOL
		//网络流不允许拖拽
		if (playerState->pauseRequest &&
			    (!strcmp(pFormatCtx->iformat->name, "rtsp") ||
			     (pFormatCtx->pb && !strncmp(url, "mmsh:", 5)))) {
			    continue;
		}
#endif
		//定位处理
		if(playerState->seekRequest) {
			int64_t seek_target = playerState->seekPos;
			int64_t seek_min = playerState->seekRel > 0 ? seek_target - playerState->seekRel + 2: INT64_MIN;
			int64_t seek_max = playerState->seekRel < 0 ? seek_target - playerState->seekRel - 2: INT64_MAX;
			// 定位 会缓存seek_min-seek_max之间的内容
	                ret = avformat_seek_file(pFormatCtx, -1, seek_min, seek_target, seek_max, playerState->seekFlags);
		        if (ret < 0) {
				av_log(NULL, AV_LOG_ERROR, "%s: error while seeking\n", url);
			}else {
				if(audioDecoder) {
					audioDecoder.flush();
				}
				if(videoDecoder) {
					videoDecoder.flush();
				}
				if(audioDevice) {
					audioDevice.flush();
				}

				//更新外部时钟
				//字节定位
				if(playerState->seekFlags & AVSEEK_FLAG_BYTE){
					mediaSync->updateExternalClock(NAN);
				}else{
					mediaSync->updateExternalClock(seek_target / (double)AV_TIME_BASE);
				}
				mediaSync->refreshVideoTimer();
			}
			attachmentRequest = 1;
		        playerState->seekRequest = 0;
		        eof = 0;
		}

		//取封面包
		if(attachmentRequest){
			if (videoDecoder && (videoDecoder->getStream()->disposition & AV_DISPOSITION_ATTACHED_PIC)) {
				AVPacket copy;
				if((ret = av_copy_packet(&copy, &videoDecoder->getStream->attached_pic)) < 0){
					break;
				}
				videoDecoder->pushPacket(&copy);
				videoDecoder->pushNullPacket();
			}
			attachmentRequest = 0;
		}

		// 如果队列中存在足够的数据包，则等待消耗
		// 备注：这里要等待一定时长的缓冲队列，要不然会导致OpenSLES播放音频出现卡顿等现象
		if (playerState->infiniteBuffer < 1 && ((audioDecoder ? audioDecoder->getMemorySize() : 0) + (videoDecoder ? videoDecoder->getMemorySize() : 0) > MAX_QUEUE_SIZE || (!audioDecoder || audioDecoder->hasEnoughPackets()) && (!videoDecoder || videoDecoder->hasEnoughPackets())) {
			continue;
		}

		//读取数据包
		ret = av_read_frame(pFormatCtx, pkt);
		if(ret < 0){
			//读取失败，判断是否结尾 pb输入数据缓存
			if((ret == AVERROR_EOF || avio_feof(pFormatCtx->pb)) && !eof) {
				if (videoDecoder != NULL) {
				    videoDecoder->pushNullPacket();
				}
				if (audioDecoder != NULL) {
				    audioDecoder->pushNullPacket();
				}
				eof = 1;
			}
			// 读取出错，则直接退出
			    if (pFormatCtx->pb && pFormatCtx->pb->error) {
				ret = -1;
				break;
			    }
			    // 如果不处于暂停状态，并且队列中所有数据都没有，则判断是否需要循环播放
			    if (!playerState->pauseRequest && (!audioDecoder || audioDecoder->getPacketSize() == 0)
				&& (!videoDecoder || (videoDecoder->getPacketSize() == 0
						      && videoDecoder->getFrameSize() == 0))) {
				if (playerState->loop) {
				    seekTo(playerState->startTime != AV_NOPTS_VALUE ? playerState->startTime : 0);
				} else if (playerState->autoExit) {
				    ret = AVERROR_EOF;
				    break;
				}
			    }
			    continue;
		}else{
			eof = 0;
		}

		// 计算pkt的pts是否处于播放范围内
		stream_start_time = pFormatCtx->streams[pkt->stream_index]->start_time;
		pkt_ts = pkt->pts == AV_NOPTS_VALUE ? pkt->dts : pkt->pts;
		//播放范围
		playInRange = playerState->duration == AV_NOPTS_VALUE
                      || (pkt_ts - (stream_start_time != AV_NOPTS_VALUE ? stream_start_time : 0)) *
                         av_q2d(pFormatCtx->streams[pkt->stream_index]->time_base)
                         - (double)(playerState->startTime != AV_NOPTS_VALUE ? playerState->startTime : 0) / 1000000
                         <= ((double)playerState->duration / 1000000);
		if (playInRange && audioDecoder && pkt->stream_index == audioDecoder->getStreamIndex()) {
		    audioDecoder->pushPacket(pkt);
		} else if (playInRange && videoDecoder && pkt->stream_index == videoDecoder->getStreamIndex()) {
		    videoDecoder->pushPacket(pkt);
		} else {
		    av_packet_unref(pkt);
		}

	}
	if (ret < 0) {
		if (playerCallback) {
		    playerCallback->onError(0x02, "error when reading packets!");
		}
	} else { // 播放完成
		if (playerCallback) {
		    playerCallback->onComplete();
		}
	}

        ALOGD("read packets thread exit!");
        return ret;
}

/**
存储方式有两种：planner和packed，第一种是平面存储，每个通道占用一个平面存储，packed是交叉存储，每个通道数据依次交叉存储，依次进行
channels 通道数量，通道的概念如左声道、右声道，音频可以是单身道和多声道，多声道的单声道的存储n倍，即多几个道就多几倍
layout 多声道中音频数据的存储方式，planner不知道哪个平面存储了哪个通道，packed也不知道是如何交替存储，的layout就是决定数据存储方式的，具体可以查看https://www.cnblogs.com/wangguchangqing/p/5851490.html
*/
int MediaPlayer::openAudioDevice(int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate) {
	AudioDeviceSpec wanted_spec, spec;
	const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
	const int next_sample_rate[] = {44100, 48000};
	//#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
	int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
	if(wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout) || !!wanted_channel_layout) {
		wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
		wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
	}
	wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
	wanted_spec.channel = wanted_nd_channels;
	wanted_spec.freq = wanted_sample_rate;
	if(wanted_spec.channel <= 0 || wanted_spec.freq <= 0) {
		av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
		return -1;
	}
	//传入的采样频率可能不是常见，选择一个和常见接近的频率
	while(next_sample_rate_idx && next_sample_rate[next_sample_rate_idx] >= wanted_sample_rate){
		next_sample_rate_idx--;
	}
	wanted_spec.format = AV_SAMPLE_FMT_S16;
	//av_log2(x) = 31 - x前导0的个数
	//AUDIO_MAX_CALLBACKS_PER_SEC音频缓存回调次数，sample应该是缓存区,不知道不确定
	wanted_spec.samples = FFMAX(AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / AUDIO_MAX_CALLBACKS_PER_SEC));
	wanted_spec.callback = audioPCMQueueCallback;
	wanted_spec.userdata = this;
	
	//打开音频设备
	while(audioDevice->open(&wanted_spec, &spec) < 0) {
		av_log(NULL, AV_LOG_WARNING, "Failed to open audio device: (%d channels, %d Hz)!\n",wanted_spec.channels, wanted_spec.freq);
		wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
		if(!wanted_spec.channels) {
			wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
			wanted_spec.channels = wanted_nb_channels;
			if(!wanted_spec.freq){
				av_log(NULL, AV_LOG_ERROR, "No more combinations to try, audio open failed\n");
		                return -1;
			}
		}
		wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
	}

	if(spec.format != AV_SAMPLE_FMT_S16) {
		av_log(NULL, AV_LOG_ERROR, "audio format %d is not supported!\n", spec.format);
	        return -1;
	}

	if(spec.channels != wanted_spec.channels) {
		wanted_channel_layout = av_get_default_channel_layout(spec.channels);
		if (!wanted_channel_layout) {
		    av_log(NULL, AV_LOG_ERROR, "channel count %d is not supported!\n", spec.channels);
		    return -1;
		}
	}

	//初始化音频重采样器
	if(!audioResampler) {
		audioResampler = new AudioResampler(playerState, audioDecoder, mediaSync);
	}
	// 设置需要重采样的参数
	audioResampler->setResampleParams(&spec, wanted_channel_layout);
}	
